
package com.monlyu.dao.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.Query;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.impl.CriteriaImpl.CriterionEntry;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.monlyu.bean.CommentBean;
import com.monlyu.dao.Dao;
import com.monlyu.dao.KeyGenerator;
import com.monlyu.dao.SqlDao;
import com.monlyu.util.ClassUtil;
import com.monlyu.util.CollectionUtils;
import com.monlyu.util.LogUtil;

@Repository("dao")
public class DaoImpl extends HibernateDaoSupport implements Dao {

    private Map<String, String> tables = new HashMap<String, String>();// 存储表，免得每次都去查找

    private SqlDao sqlDao;

    public void setSqlDao(SqlDao sqlDao){
        this.sqlDao = sqlDao;
    }

    @Override
    public void clear(){
        getSession().flush();
        getSession().clear();
    }

    @Override
    public void delete(Object entities){
        if (CollectionUtils.isSubCollection(entities)) {
            getHibernateTemplate().deleteAll((Collection<?>) entities);
        } else {
            getHibernateTemplate().delete(entities);
        }
    }

    @Override
    public <T> void delete(final Class<T> entitiy, final Serializable id){
        final Object object = get(entitiy, id);
        if (object != null) {
            delete(object);
        } else {
            logger.warn("删除无效列:实体:" + entitiy.getName() + "->Id号:" + id);
        }
    }

    @Override
    public int execute(String hql, Object... args){
        return createQuery(hql, args).executeUpdate();
    }

    @Override
    @SuppressWarnings("unchecked")
    public List find(String hql, Object... args){
        return createQuery(hql, args).list();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> List<T> find(Class<T> clazz, String property, Object value){
        Criteria criteria = initCriteria(clazz);
        criteria.add(Restrictions.eq(property, value));
        return criteria.list();
    }

    @Override
    public List<?> findLimit(int limit, String hql, Object... args){
        return createQuery(hql, args).setMaxResults(limit).list();
    }

    @Override
    public <T> List<T> findLimit(int limit, Class<T> clazz, String property, Object value){
        return null;
    }

    @Override
    public Object findOne(String hql, Object... args){
        return CollectionUtils.head(findLimit(1, hql, args));
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T findOne(Class<T> clazz, String property, Object value){
        Criteria criteria = initCriteria(clazz);
        criteria.add(Restrictions.eq(property, value));
        List<T> list = criteria.setMaxResults(1).list();
        return list.isEmpty() ? null : list.get(0);
    }

    @Override
    public <T> T get(Class<T> clazz, Serializable id){
        return getHibernateTemplate().get(clazz, id);
    }

    /**
     * 查找所有断T对象
     * 
     * @since 2009-12-29
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> find(Class<T> clazz){
        return initCriteria(clazz).list();
    }

    @Override
    public Serializable getId(Object entity){
        // if (entity instanceof Idable) {
        // return ((Idable) entity).getId();
        // }
        return getSessionFactory().getClassMetadata(ClassUtil.getClass(entity)).getIdentifier(entity, EntityMode.POJO);
    }

    @Override
    public Criteria initCriteria(Class<?> clazz){
        return getSession().createCriteria(clazz);
    }

    @Override
    public void insertOrUpdate(Object entities){
        if (CollectionUtils.isSubCollection(entities)) {
            for (Object entity : (Collection<?>) entities) {
                insertOrUpdate(entity);
            }
        } else {
            saveOrUpdate(entities);
        }
    }

    @Override
    public void load(Object objVo, Serializable id){
        getHibernateTemplate().load(objVo, id);
    }

    @Override
    public void load(Object vo){
        getHibernateTemplate().load(vo, getId(vo));
    }

    @Override
    public void setId(Object entity, Serializable id){}

    // =====================Private Methods ==============//
    /** 创建一个查询 */
    private Query createQuery(final String hql, final Object[] args){
        final Query query = getSession().createQuery(hql);
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                query.setParameter(i, args[i]);
            }
        }
        return query;
    }

    /**
     * 更新记录
     * 
     * @param entity
     */
    private void saveOrUpdate(Object entity){
        if (hasId(entity)) {
            update(entity);
        } else {
            save(entity);
        }
    }

    /**
     * 检查是否有ID
     * 
     * @param entity
     * @return
     */
    private boolean hasId(Object entity){
        return getId(entity) != null;
    }

    @Override
    public void update(Object entities){
        getHibernateTemplate().update(entities);
    }

    /**
     * 保存对象
     */
    public Serializable save(Object entity){
        checkId(entity);
        return getHibernateTemplate().save(entity);
    }

    /**
     * 检查ID
     * 
     * @author monlyu
     * @since 2010-1-1
     */
    private boolean checkId(Object entity){
        return KeyGenerator.createKeyGenerator(getSessionFactory()).checkId(entity);
    }

    /**
     * 初始化数据专用
     * 
     * @since 2009-12-30
     */

    @Override
    public void initData(Object entity){
        if (CollectionUtils.isSubCollection(entity)) {
            for (Object singnal : (Collection<?>) entity) {
                initData(singnal);
            }
        } else {
            save(entity);
        }
    }

    @Override
    public SqlDao getSqlDao(){
        return sqlDao;
    }

    @Override
    public void addComment(List<CommentBean> beans){
        try {
            for (CommentBean bean : beans) {
                if (bean.isMysql()) {
                    if (bean.isHead()) {
                        sqlDao.excute(bean.toString());
                    } else {
                        String ddl = null;
                        if ((ddl = tables.get(bean.getTable())) == null) {
                            ddl = sqlDao.getDDL(bean.toString());
                            tables.put(bean.getTable(), ddl);
                        }
                        sqlDao.excute(CommentBean.mysqlComent(ddl, bean));
                    }
                } else {
                    sqlDao.excute(bean.toString());
                }
            }
            sqlDao.clearSession();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void save2key(Object entity){
        if (CollectionUtils.isSubCollection(entity)) {
            for (Object object : (Collection<?>) entity) {
                save2key(object);
            }
        } else {
            getHibernateTemplate().save(entity);
        }
    }

    /** 此方法来之不易阿 */
    @Override
    public Integer countRow(Criteria criteria){
        CriteriaImpl impl = (CriteriaImpl) criteria;
        try {
            Criteria countCriteria = initCriteria(Class.forName(impl.getEntityOrClassName()));
            for (Iterator<?> iterator = impl.iterateExpressionEntries(); iterator.hasNext();) {
                CriterionEntry type = (CriterionEntry) iterator.next();
                countCriteria.add(type.getCriterion());
            }
            return (Integer) countCriteria.setProjection(Projections.rowCount()).uniqueResult();
        }
        catch (Exception e) {
            LogUtil.log(getClass(), e);
        }
        return 0;
    }

    @Override
    public <T> List<T> get(Class<T> clz, String id, List<Long> mid){
        if (mid.size() == 0) {
            return Collections.EMPTY_LIST;
        }
        Criteria criteria = initCriteria(clz);
        criteria.add(Restrictions.in(id, mid));
        return criteria.list();
    }
}